package org.loofer.ext.component.thread;

import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.atomic.AtomicLong;

public class PriorityThreadPool extends ThreadPool {
    private static final PriorityThreadPool sDefaultInstance = new PriorityThreadPool();

    public static class Priority {
        public static final Priority HIGH = new Priority(1, false);
        public static final Priority LOW = new Priority(-1, true);
        public static final Priority NORMAL = new Priority(0, true);
        public final boolean fifo;
        public final int priority;

        public Priority(int priority, boolean fifo) {
            this.priority = priority;
            this.fifo = fifo;
        }
    }

    private static class PriorityJob<T> implements Job<T>, Comparable<PriorityJob> {
        private static final AtomicLong SEQ = new AtomicLong(0);
        private final boolean mFifo;
        private final Job<T> mJob;
        private final int mPriority;
        private final long mSeqNum = SEQ.getAndIncrement();

        public PriorityJob(Job<T> job, int priority, boolean fifo) {
            this.mJob = job;
            this.mPriority = priority;
            this.mFifo = fifo;
        }

        public T run(JobContext jc) {
            return this.mJob.run(jc);
        }

        public int compareTo(PriorityJob another) {
            if (this.mPriority > another.mPriority) {
                return -1;
            }
            return this.mPriority < another.mPriority ? 1 : subCompareTo(another);
        }

        private int subCompareTo(PriorityJob another) {
            int result = this.mSeqNum < another.mSeqNum ? -1 : this.mSeqNum > another.mSeqNum ? 1 : 0;
            return this.mFifo ? result : -result;
        }
    }

    public static PriorityThreadPool getDefault() {
        return sDefaultInstance;
    }

    public PriorityThreadPool() {
        this("priority-thread-pool", 4);
    }

    public PriorityThreadPool(String name, int poolSize) {
        super(name, poolSize, poolSize, new PriorityBlockingQueue());
    }

    public <T> Future<T> submit(Job<T> job, FutureListener<T> listener, Priority priority) {
        if (priority == null) {
            priority = Priority.NORMAL;
        }
        return super.submit(new PriorityJob(job, priority.priority, priority.fifo), listener);
    }

    public <T> Future<T> submit(Job<T> job, Priority priority) {
        return submit(job, null, priority);
    }

    public <T> Future<T> submit(Job<T> job, FutureListener<T> listener) {
        return submit(job, listener, null);
    }

    public <T> Future<T> submit(Job<T> job) {
        return submit(job, null, null);
    }
}
